<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>es5 重写函数原型对象</title>
  <!-- 
  在 JavaScript ES5 中，重写函数的原型对象是一个常见的操作，它允许我们扩展或修改函数的行为。
  1. 函数原型的基本概念
  - 在 JavaScript 中，每个函数都有一个  `prototype`  属性，它是一个对象，用于定义该函数创建的实例共享的属性和方法。
  - 当使用  `new`  关键字创建一个对象时，该对象的  `[[Prototype]]`  会指向构造函数的  `prototype` 。

  2. 重写函数原型对象
  重写函数的原型对象可以通过直接赋值或扩展原型的方法来实现。以下是几种常见的方式：

   2.1 直接赋值
  可以直接将一个新的对象赋值给函数的  `prototype`  属性，从而重写它。
  function Person(name) {
      this.name = name;
  }

  // 原始原型方法
  Person.prototype.sayHello = function() {
      console.log("Hello, " + this.name);
  };

  // 重写原型对象
  Person.prototype = {
      greet: function() {
          console.log("Greetings, " + this.name);
      }
  };

  var alice = new Person("Alice");
  alice.sayHello(); // TypeError: alice.sayHello is not a function
  alice.greet();    // 输出: Greetings, Alice
  在这个例子中，重写  `Person.prototype`  后，原来的  `sayHello`  方法不再可用。

   2.2 扩展原型
  可以在原有的原型对象上添加或修改方法，而不是完全重写。
  function Person(name) {
      this.name = name;
  }

  Person.prototype.sayHello = function() {
      console.log("Hello, " + this.name);
  };

  // 扩展原型对象
  Person.prototype.sayGoodbye = function() {
      console.log("Goodbye, " + this.name);
  };

  var bob = new Person("Bob");
  bob.sayHello();    // 输出: Hello, Bob
  bob.sayGoodbye();  // 输出: Goodbye, Bob
  在这个例子中，我们保留了原有的  `sayHello`  方法，并添加了一个新的  `sayGoodbye`  方法。

  3. 注意事项
  - 重写原型的影响：重写原型会影响到通过该构造函数创建的新实例，但不会影响已经创建的实例。
  - 共享方法：通过原型定义的方法是共享的，这意味着所有实例都可以访问这些方法，而不需要在每个实例中单独创建。
  - 使用  `Object.create()` ：如果需要创建一个新的原型对象，可以使用  `Object.create()` ，这可以帮助你在不影响原有原型的情况下创建新的原型。

  4. 总结
  重写函数原型对象是 JavaScript 中一个强大的特性，它允许开发者灵活地扩展和修改对象的行为。理解如何重写和扩展原型对于实现继承、代码重用和优化性能非常重要。在进行原型重写时，需要注意对现有实例的影响，以及如何有效地管理共享方法。
   -->
</head>
<body>

  <script>

    function Person() {}

    console.log(Person.prototype); // 输出: Person {}
    // 1.在原有的原型对象上添加新的属性
    // Person.prototype.message = "Hello Person"
    // Person.prototype.info = { name: "prototype", age: 30 }
    // Person.prototype.running = function() {}
    // Person.prototype.eating = function() {}
    // console.log(Person.prototype)
    // console.log(Object.keys(Person.prototype))

    // 2. 直接重写原型对象
    Person.prototype = {
      message: "Hello Person",
      info: { name: "prototype", age: 30 },
      running: function() {},
      eating: function() {},
      // constructor: Person // 重写原型对象后，需要手动设置 constructor 属性
    }
    Object.defineProperty(Person.prototype, 'constructor', {
      enumerable: false, // 不可枚举(不可遍历)
      configruable: true, // 可配置(可删除)
      writable: true, // 可写(可修改)
      value: Person // 指向构造函数
    })
    
    console.log(Person.prototype)
    console.log(Object.keys(Person.prototype))
    const p = new Person()
    console.log(p.message)
    console.log(p.info)
    p.running()
    p.eating()

  </script>
  
</body>
</html>